#include <xmmintrin.h>
#include <emmintrin.h>
#include <pmmintrin.h>
#include "define.h"
#include "mkl.h"


//Set zero function for SP and DP
template <class T>
T _sse_setzero(){
	return _mm_setzero_pd();
}

template <>
__m128d _sse_setzero<__m128d>(){
	return _mm_setzero_pd();
}

template <>
__m128 _sse_setzero<__m128>(){
	return _mm_setzero_ps();
}


//Set function for both SP and DP
template <class T, class U>
U _sse_set(T val1, T val2){
	return _sse_setzero<U>();
}

template <class T, class U>
U _sse_set(T val1, T val2, T val3, T val4){
	return _sse_setzero<U>();
}

template <>
__m128d _sse_set<double, __m128d>(double val1, double val2){
	return _mm_set_pd(val1, val2);
}

template <>
__m128 _sse_set<float, __m128>(float val1, float val2, float val3, float val4){
	return _mm_set_ps(val1, val2, val3, val4);
}

//CIS function for SP and DP
template <class T>
void _sse_CIS(int n, T *a, std::complex<T> *y){
	vzCIS(n, a, (MKL_Complex16 *)y);
}

template <>
void _sse_CIS<double>(int n, double *a, std::complex<double> *y){
	vzCIS(n, a, (MKL_Complex16 *)y);
}

template <>
void _sse_CIS<float>(int n, float *a, std::complex<float> *y){
	vcCIS(n, a, (MKL_Complex8 *)y);
}

//EXP function for SP and DP
template <class T>
void _sse_EXP(int n, T *a, T *y){
	vdExp(n, a, y);
}

template <>
void _sse_EXP<double>(int n, double *a, double *y){
	vdExp(n, a, y);
}

template <>
void _sse_EXP<float>(int n, float *a, float *y){
	vsExp(n, a, y);
}

//Load SSE function for SP and DP
template <class T, class U>
U _sse_load (const T *add){
	return _mm_load_pd(add);
}

template <>
__m128d _sse_load<double> (const double *add){
	return _mm_load_pd(add);
}

template <>
__m128 _sse_load<float> (const float *add){
	return _mm_load_ps(add);
}

//Load1 SSE function for SP and DP
template <class T, class U>
U _sse_load1(const T *add){
	return _mm_load1_pd(add);
}

template <>
__m128d _sse_load1(const double *add){
	return _mm_load1_pd(add);
}

template <>
__m128 _sse_load1(const float *add){
	return _mm_load1_ps(add);
}

//Set1 SSE function for both SP and DP
template <class T, class U>
U _sse_set1(T val){
	return _mm_set1_pd(val);
}

template <>
__m128d _sse_set1(double val){
	return _mm_set1_pd(val);
}

template <>
__m128 _sse_set1(float val){
	return _mm_set1_ps(val);
}

//Store instruction for both SP and DP
template <class T, class U>
void _sse_store(T *add, U val){
	_mm_store_pd(add,val);
}

template <>
void _sse_store(double *add, __m128d val){
	_mm_store_pd(add,val);
}

template <>
void _sse_store(float *add, __m128 val){
	_mm_store_ps(add,val);
}

//Add instruction for both SP and DP
template <class T>
T _sse_add(T val1, T val2){
	return _mm_add_pd(val1, val2);
}

template <>
__m128d _sse_add(__m128d val1, __m128d val2){
	return _mm_add_pd(val1, val2);
}

template <>
__m128 _sse_add(__m128 val1, __m128 val2){
	return _mm_add_ps(val1, val2);
}

//Sub instruction for both SP and DP
template <class T>
T _sse_sub(T val1, T val2){
	return _mm_sub_pd(val1, val2);
}

template <>
__m128d _sse_sub(__m128d val1, __m128d val2){
	return _mm_sub_pd(val1, val2);
}

template <>
__m128 _sse_sub(__m128 val1, __m128 val2){
	return _mm_sub_ps(val1, val2);
}

//Mul instruction for both SP and DP
template <class T>
T _sse_mul(T val1, T val2){
	return _mm_mul_pd(val1, val2);
}

template <>
__m128d _sse_mul(__m128d val1, __m128d val2){
	return _mm_mul_pd(val1, val2);
}

template <>
__m128 _sse_mul(__m128 val1, __m128 val2){
	return _mm_mul_ps(val1, val2);
}


//Div instruction for both SP and DP
template <class T>
T _sse_div(T val1, T val2){
	return _mm_div_pd(val1, val2);
}

template <>
__m128d _sse_div(__m128d val1, __m128d val2){
	return _mm_div_pd(val1, val2);
}

template <>
__m128 _sse_div(__m128 val1, __m128 val2){
	return _mm_div_ps(val1, val2);
}


//Half add instruction for both SP and DP
template <class T>
T _sse_hadd(T val1, T val2){
	return _mm_add_pd(val1, val2);
}

template <>
__m128d _sse_hadd(__m128d val1, __m128d val2){
	return _mm_hadd_pd(val1, val2);
}

template <>
__m128 _sse_hadd(__m128 val1, __m128 val2){
	return _mm_hadd_ps(val1, val2);
}

//Sqrt instruction for both SP and DP
template <class T>
T _sse_sqrt(T val){
	return _mm_sqrt_pd(val);
}

template <>
__m128d _sse_sqrt(__m128d val){
	return _mm_sqrt_pd(val);
}

template <>
__m128 _sse_sqrt(__m128 val){
	return _mm_sqrt_ps(val);
}


